Skip to content

SEP 8 -- 日志打印规范

Head

  • Author: larry
  • Status: active
  • Type: Standards
  • Created: 2017-09-06

摘要

日志的目的主要有两种:排查故障,数据分析。为了更好的完成这些目的,对日志做统一的规范。

修改历史

v1

初始版本

v2

  1. access

    字段顺序做了微调,cms_key放到了后面
    增加输入输出长度字段
    transaction_id改名为trace_id,可以由业务代码自行设置
    增加extra_value字段,做一些临时存放需求。
    username前加userid
    
  2. app

    transaction_id改名为trace_id,可以由业务代码自行设置
    过长的返回增加"..."后缀
    http_content_length改为param_string_length
    增加userid
    cms_key移动位置
    
  3. error

    error日志包含warning
    
  4. 滚动

    删除历史过久的日志、设置固定日期数
    

日志类型

常见日志类型有以下几种。

  1. access_log:访问日志。一次请求一条日志,主要目的是为了数据分析。
  2. app_log:应用日志。一次请求多条日志,主要目的就是排查故障。
  3. sys_log:系统日志。通常指的是运行应用程序的容器本身的日志,比如uwsgi,nginx等等。
  4. error_log:错误日志。app_log中一定有错误日志信息,通常还会单独有一个错误日志文件,便于集中查看错误量。warning也放入error中。
  5. 其他:根据需要还可以有其他各种日志。

我们的系统至少应该有前四种。

access_log(访问日志)

访问日志的主要目的是为了后期的数据分析,所以在设计上尽量格式通用,字段丰富。

Common Log Format

日志使用CLF格式(Common Log Format)。CLF是目前目前主流的访问日志格式,apache和nginx以及其他web服务器都使用这种,有大量第三方日志分析工具都支持这种格式,使用这种格式便于后续的数据分析。

  1. 一行一条日志。
  2. 一条日志由多个字段组成,字段间用space或者tab分割,允许多个。
  3. 字段可以由双引号(")包裹起来,应对本身有空格的字段,或者有些特殊字段。
  4. 特殊符号转义。'" ' -> '"\ ','\' -> '\\'。空格包含space和tab两种。
  5. 如果字段没有值或者无意义,用'-'。

日志字段选取

字段的选取上,尽量全面。包含几类。

  1. 头部:系统公共字段,业务公共字段,版本。
  2. 输入:原始输入信息,未经任何转换的格式
  3. 输出:原始输出信息
  4. 其他:中间统计变量,或者其他必要信息

格式定义

v1: [time_local version] request_id processing_time_ms group_id cms_key station_id kid sid username transaction_id "request_path" request_method "param_string_striped_by_max_length" status ret_code "ret_msg" "$ret_body_striped_by_max_length"

v2: [time_local version] request_id processing_time_ms group_id station_id kid sid cms_key userid username trace_id "request_path" request_method param_string_length "param_string_striped_by_max_length" status ret_code "ret_msg" ret_body_length "$ret_body_striped_by_max_length" "extra_value"

v3: [time_local version request_id return_code group_id ] { "http": { "request_method": "request_uri": "status": }, "ctx": { "request_id": "group_id": "station_id": "kid": "sid": "cms_key": "userid": "username": "request_time": "trace_id": }, "biz": { "response_code": "response_msg": "request_data": { "text": "len": "file_len": "file_ref": } "response_data": { "text": "len": } } }

说明

  1. version:1开始,每次格式变化+1,要保留历史格式文档。
  2. request_id:请求唯一id,如果http的X-Guanmai-Request-Id头字段有值就用,没有就自己生成一个,规则:md5(uuid)。
  3. X-Guanmai-Request-Id:特别定义这样一个头字段,用来在串行请求中传递请求唯一id,以便把请求串起来。
  4. param_string_striped_by_max_length这种字段,最长设置一个配置值,默认到512字节。
  5. extra_value: 增加了一个额外的备用字段,便于做一些临时存放需求。

app_log(应用日志)

日常的应用日志,打印内容最全面。

格式定义

基本格式:[日志头部]:日志内容(限制最长)...

日志头部:

v1: [time_local log_level request_id group_id cms_key station_id kid sid username transaction_id]

v2: [time_local log_level request_id group_id station_id kid sid cms_key userid username trace_id]

日志内容:每个请求,至少会打印两行日志:输入和输出。当然,其他内容根据需要打印。

日志内容打印建议

  1. 输入

    在框架中打印。格式:request_path,request_method,param_string_length,param_string

    这里的param_string,如果是get,就是原始的query_string。如果是post,就是body的内容,尽量保持原样。

  2. 输出

    在框架中打印。格式:ret_body_length,ret_body_string

  3. 其他日志

    所有与其他模块的交互需要打印日志:数据库,redis,其他微服务

    数据库操作/redis操作:需要打印执行的sql语句,或者mongo的请求和响应参数。如果不能确认在执行后能安全打印,那么执行前后各打印一条。
    如果请求和响应的数据量太大,那么最好在打印内容中,打印数据长度和数据的摘要,以备排查故障。数据太长了会被统一的日志配置给截断,打出来也显示不完整。
    

    分支判断与流程走向

    能从输入参数简单推断出的逻辑流程、可以不打印。如果是要用第三方系统查询结果,配合其他数据做复杂结算才能推断出流程分支的,建议把作为分支流程判断依据的计算结果打印出来。因为实际排查问题的时候,人工很难做出复杂的计算,也就很难判断分支走向了。
    

    debug日志

    前面说的日志都是info,自己开发中需要的日志可以打印的更详细。debug日志是随时可以删除的日志。
    

    其他 其他认为需要的日志。如果不知道怎么选择,可以观察debug联调时候打印过的日志,从中发现一些适合变为info的。

  4. 逗号分割

    日志内容部分用逗号分割,不是空格。因为这里是随意打印,空格是值的可能性很大,所以不要用空格做分割,避免误判。

  5. trace_id

    trace_id是留给业务自行填入的一个字段,比如订单类接口,业务可以把订单号填进去,便于搜索日志。

sys_log(系统日志)

在我们的系统中是uwsgi,可以不用改名,就用uswgi.log。

error_log(错误日志)

在app_log之外,所有的exception再单独打印一个日志,格式与app_log相同。

请求唯一ID(request-id)

在http报文中增加了一个head:X-Guanmai-Request-Id,标示一个请求的请求唯一id。

nginx配置

为了把nginx的access_log和我们自己的access_log对应起来,同一个请求,在两个日志中的记录应该用相同的request-id。

在nginx log配置中,增加$request_id变量。因为这个变量在1.11以上版本才支持,所以必须先升级nginx到最新。

nginx转给uwsgi的请求中,加入一个头字段,X-Guanmai-Request-Id=$request_id

微服务调用rpcclient

增加头字段,X-Guanmai-Request-Id=request_id